﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Text;
using Raona.Code.Contracts.Inspection;
using Raona.Code.Model;

namespace Raona.Code.AssemblyInspection
{
    public class AssemblyOperationContractInspector3 : AssemblyOperationContractInspectorBase, IOperationContractInspector
    {
        #region IOperationContractInspector Members

        public Dictionary<string, Model.Namespace> GetInterfaces(string clientBase, string serviceContractsNamespace)
        {
            Dictionary<string, Model.Namespace> namespaces = new Dictionary<string, Namespace>();

            IEnumerable<IGrouping<string, Type>> serviceContractsNamespaceGroups = this.GetServiceContractsNamespaceGroups(
                clientBase,
                serviceContractsNamespace);
            foreach (IGrouping<string, Type> serviceContractsNamespaceGroup in serviceContractsNamespaceGroups)
            {
                Namespace nameSpace = new Namespace { Name = serviceContractsNamespaceGroup.Key };
                List<Interface> interfaces = new List<Interface>();
                foreach (Type type in serviceContractsNamespaceGroup)
                {
                    Interface itf = new Interface { Name = type.Name, ClientBaseName = clientBase, ServiceContractNamespace = serviceContractsNamespace };
                    //this.ReportProgress(itf);
                    IEnumerable<MethodInfo> methodInfos = type.GetMethods().Where(m => m.GetCustomAttributes(typeof(OperationContractAttribute), false).Length > 0);
                    itf.AddMethods(methodInfos.Select(m =>
                        new Method
                        {
                            Name = m.Name,
                            OverloadedName = this.GetOverloadedName(m),
                            ReturnType = this.GetTypeName(m.ReturnType),
                            Parameters = m.GetParameters().Select(p => new Parameter { Name = p.Name, Type = this.GetTypeName(p.ParameterType) }).ToList()
                        }));
                    IEnumerable<ServiceKnownTypeAttribute> knownTypesAttributes = type.GetCustomAttributes(typeof(ServiceKnownTypeAttribute), false).Cast<ServiceKnownTypeAttribute>();
                    itf.AddServiceKnownTypes(knownTypesAttributes.Select(a => a.Type.FullName));

                    interfaces.Add(itf);
                }
                nameSpace.Interfaces.AddRange(interfaces);
                namespaces.Add(nameSpace.Name, nameSpace);
            }

            return namespaces;
        }

        public void GetInterfacesAsync(string clientBase, string serviceContractsNamespace)
        {
            throw new NotImplementedException();
        }

        public void CancelGetInterfaces()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            
        }

        #endregion

        #region Instance Methods (Private)

        private IEnumerable<IGrouping<string, Type>> GetServiceContractsNamespaceGroups(
            string clientBase, 
            string serviceContractsNamespace)
        {
            Assembly contractsAssembly = Assembly.LoadFrom(this.contractsAssemblyPath);
            // Get all public types in the provided assembly
            Type[] types = contractsAssembly.GetExportedTypes();
            // Get interface types only
            IEnumerable<Type> interfaces = types.Where(t => t.IsInterface && t.IsPublic);
            // Get interfaces marked as ServiceContract
            IEnumerable<Type> serviceContracts =
                    from i in interfaces
                    let attributes = i.GetCustomAttributes(typeof(ServiceContractAttribute), true).Cast<ServiceContractAttribute>()
                    where attributes.Count() > 0
                    select i;
            // Group interfaces by namespace
            IEnumerable<IGrouping<string, Type>> serviceContractsNamespaceGroups = serviceContracts.GroupBy(i => i.Namespace);

            return serviceContractsNamespaceGroups;
        }

        #endregion
    }
}
